package com.freexiaoyu.freevideoplay.widget;

import android.content.Context;
import android.media.MediaPlayer;
import android.os.CountDownTimer;
import android.util.AttributeSet;
import android.view.View;
import android.widget.RelativeLayout;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.freexiaoyu.freevideoplay.utils.LogUtil;
import com.google.android.exoplayer2.DeviceInfo;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.MediaMetadata;
import com.google.android.exoplayer2.PlaybackException;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.Tracks;
import com.google.android.exoplayer2.audio.AudioAttributes;
import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.text.CueGroup;
import com.google.android.exoplayer2.trackselection.TrackSelectionParameters;
import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.exoplayer2.video.VideoSize;

import java.util.Map;

/**
 * createTime   ：2024/6/21 9:14
 * createBy     ：lkl
 */
public class MyVideoView extends RelativeLayout {
    /**
     * 准备时长，超过这个时间就重试加载
     */
    private static final long INIT_TIME_OUT = 1000 * 15;
    /**
     * 进度回调间隔
     */
    private final static long TimeInterval = 1000;
    //计时器
    private CountDownTimer timer;
    //准备就绪后开始播放（默认false）
    private boolean autoPlay = false;
    //视频时长
    private long duration;
    //回调
    private OnVideoCallBack onVideoCallBack;
    //
    private int videoWidth, videoHeight;
    //是否准备就绪了
    private boolean isReady = false;
    //暂停时的位置、需要恢复的位置
    private long currentPosition = -1;
    //出错时，重试的次数
    private int retryCount = 0;
    //视频连接
    private String videoPath;
    //头部信息
    private Map<String, String> header;
    private final ExoPlayer player;
    private final Context context;

    public MyVideoView(Context context) {
        this(context, null, 0, 0);
    }

    public MyVideoView(Context context, AttributeSet attrs) {
        this(context, attrs, 0, 0);
    }

    public MyVideoView(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public MyVideoView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        this.context = context;
        this.player = new ExoPlayer.Builder(context).build();
        PlayerView videoView = new PlayerView(context);
        videoView.setPlayer(player);
        videoView.setUseController(false);
        videoView.setClickable(false);
        videoView.setFocusableInTouchMode(false);
        RelativeLayout.LayoutParams lp = new LayoutParams(-1, -1);
        lp.addRule(CENTER_IN_PARENT);
        videoView.setLayoutParams(lp);
        addView(videoView);
        initListener();
    }

    private void initListener() {
        player.addListener(new Player.Listener() {
            @Override
            public void onEvents(@NonNull Player player, @NonNull Player.Events events) {
                Player.Listener.super.onEvents(player, events);
            }

            @Override
            public void onTimelineChanged(@NonNull Timeline timeline, int reason) {
                Player.Listener.super.onTimelineChanged(timeline, reason);
            }

            @Override
            public void onMediaItemTransition(@Nullable MediaItem mediaItem, int reason) {
                Player.Listener.super.onMediaItemTransition(mediaItem, reason);
            }

            @Override
            public void onTracksChanged(@NonNull Tracks tracks) {
                Player.Listener.super.onTracksChanged(tracks);
            }

            @Override
            public void onMediaMetadataChanged(@NonNull MediaMetadata mediaMetadata) {
                Player.Listener.super.onMediaMetadataChanged(mediaMetadata);
            }

            @Override
            public void onPlaylistMetadataChanged(@NonNull MediaMetadata mediaMetadata) {
                Player.Listener.super.onPlaylistMetadataChanged(mediaMetadata);
            }

            @Override
            public void onIsLoadingChanged(boolean isLoading) {
                Player.Listener.super.onIsLoadingChanged(isLoading);
            }

            @Override
            public void onAvailableCommandsChanged(@NonNull Player.Commands availableCommands) {
                Player.Listener.super.onAvailableCommandsChanged(availableCommands);
            }

            @Override
            public void onTrackSelectionParametersChanged(@NonNull TrackSelectionParameters parameters) {
                Player.Listener.super.onTrackSelectionParametersChanged(parameters);
            }

            @Override
            public void onPlaybackStateChanged(int playbackState) {
                Player.Listener.super.onPlaybackStateChanged(playbackState);
                switch (playbackState) {
                    case Player.STATE_READY:
                        onPreparedCallBack();
                        if (onVideoCallBack != null) {
                            onVideoCallBack.onBufferEnd();
                        }
                        if (timer != null) {
                            timer.cancel();
                        }
                        timeOut.cancel();
                        break;
                    case Player.STATE_BUFFERING:
                        if (onVideoCallBack != null) {
                            onVideoCallBack.onBufferStart();
                        }
                        if (timer != null) {
                            timer.cancel();
                        }
                        timeOut.cancel();
                        timeOut.start();
                        break;
                    case Player.STATE_ENDED:
                        if (onVideoCallBack != null) {
                            onVideoCallBack.onComplete();
                        }
                        if (timer != null) {
                            timer.cancel();
                        }
                        break;
                    case Player.STATE_IDLE:
                        //玩家是空闲的，这意味着它只拥有有限的资源。播放器在播放媒体之前必须做好准备。
                        LogUtil.d("初始状态");
                        if (timer != null) {
                            timer.cancel();
                        }
                        break;
                    default:
                        LogUtil.i("播放器状态：" + playbackState);
                        break;
                }
            }

            @Override
            public void onPlayWhenReadyChanged(boolean playWhenReady, int reason) {
                Player.Listener.super.onPlayWhenReadyChanged(playWhenReady, reason);
            }

            @Override
            public void onPlaybackSuppressionReasonChanged(int playbackSuppressionReason) {
                Player.Listener.super.onPlaybackSuppressionReasonChanged(playbackSuppressionReason);
            }

            @Override
            public void onIsPlayingChanged(boolean isPlaying) {
                Player.Listener.super.onIsPlayingChanged(isPlaying);
                if (onVideoCallBack != null) {
                    if (isPlaying) {
                        onVideoCallBack.onStart();
                        if (timer != null) {
                            timer.start();
                        }
                    } else {
                        onVideoCallBack.onPause();
                        if (timer != null) {
                            timer.cancel();
                        }
                    }
                }
            }

            @Override
            public void onRepeatModeChanged(int repeatMode) {
                Player.Listener.super.onRepeatModeChanged(repeatMode);
            }

            @Override
            public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
                Player.Listener.super.onShuffleModeEnabledChanged(shuffleModeEnabled);
            }

            @Override
            public void onPlayerError(@NonNull PlaybackException error) {
                Player.Listener.super.onPlayerError(error);
            }

            @Override
            public void onPlayerErrorChanged(@Nullable PlaybackException error) {
                Player.Listener.super.onPlayerErrorChanged(error);
            }

            @Override
            public void onPositionDiscontinuity(@NonNull Player.PositionInfo oldPosition, @NonNull Player.PositionInfo newPosition, int reason) {
                Player.Listener.super.onPositionDiscontinuity(oldPosition, newPosition, reason);
            }

            @Override
            public void onPlaybackParametersChanged(@NonNull PlaybackParameters playbackParameters) {
                Player.Listener.super.onPlaybackParametersChanged(playbackParameters);
            }

            @Override
            public void onSeekBackIncrementChanged(long seekBackIncrementMs) {
                Player.Listener.super.onSeekBackIncrementChanged(seekBackIncrementMs);
            }

            @Override
            public void onSeekForwardIncrementChanged(long seekForwardIncrementMs) {
                Player.Listener.super.onSeekForwardIncrementChanged(seekForwardIncrementMs);
            }

            @Override
            public void onMaxSeekToPreviousPositionChanged(long maxSeekToPreviousPositionMs) {
                Player.Listener.super.onMaxSeekToPreviousPositionChanged(maxSeekToPreviousPositionMs);
            }

            @Override
            public void onAudioSessionIdChanged(int audioSessionId) {
                Player.Listener.super.onAudioSessionIdChanged(audioSessionId);
            }

            @Override
            public void onAudioAttributesChanged(@NonNull AudioAttributes audioAttributes) {
                Player.Listener.super.onAudioAttributesChanged(audioAttributes);
            }

            @Override
            public void onVolumeChanged(float volume) {
                Player.Listener.super.onVolumeChanged(volume);
            }

            @Override
            public void onSkipSilenceEnabledChanged(boolean skipSilenceEnabled) {
                Player.Listener.super.onSkipSilenceEnabledChanged(skipSilenceEnabled);
            }

            @Override
            public void onDeviceInfoChanged(@NonNull DeviceInfo deviceInfo) {
                Player.Listener.super.onDeviceInfoChanged(deviceInfo);
            }

            @Override
            public void onDeviceVolumeChanged(int volume, boolean muted) {
                Player.Listener.super.onDeviceVolumeChanged(volume, muted);
            }

            @Override
            public void onVideoSizeChanged(@NonNull VideoSize videoSize) {
                Player.Listener.super.onVideoSizeChanged(videoSize);
            }

            @Override
            public void onSurfaceSizeChanged(int width, int height) {
                Player.Listener.super.onSurfaceSizeChanged(width, height);
            }

            @Override
            public void onRenderedFirstFrame() {
                Player.Listener.super.onRenderedFirstFrame();
            }

            @Override
            public void onCues(@NonNull CueGroup cueGroup) {
                Player.Listener.super.onCues(cueGroup);
            }

            @Override
            public void onMetadata(@NonNull Metadata metadata) {
                Player.Listener.super.onMetadata(metadata);
            }
        });
    }

    private void onPreparedCallBack() {
        if (isReady) {
            LogUtil.i("无需重复调用准备就绪回调");
            return;
        }
        LogUtil.i("准备就绪！！！");
        timeOut.cancel();
        isReady = true;
        VideoSize size = player.getVideoSize();
        videoWidth = size.width;
        videoHeight = size.height;
        duration = player.getDuration();
        if (timer != null) {
            timer.cancel();
        }
        timer = new CountDownTimer(duration, TimeInterval) {
            @Override
            public void onTick(long left) {
                if (onVideoCallBack != null) {
                    currentPosition = player.getCurrentPosition();
                    onVideoCallBack.onProgress(currentPosition, duration);
                }
            }

            @Override
            public void onFinish() {

            }
        };
        if (onVideoCallBack != null) {
            onVideoCallBack.onPrepared(duration);
        }
        if (currentPosition != -1) {
            //恢复播放
            LogUtil.i("恢复播放，恢复进度：" + currentPosition);
            seekTo(currentPosition);
            start();
        } else {
            if (autoPlay) {
                start();
            }
        }
    }

    public void timePause() {
        if (timer != null) {
            timer.cancel();
        }
    }

    public void timeContinue() {
        if (timer != null) {
            timer.cancel();
            timer.start();
        }
    }

    public boolean isReady() {
        return isReady;
    }

    public void setAutoPlay(boolean autoPlay) {
        this.autoPlay = autoPlay;
    }

    public int getVideoWidth() {
        return videoWidth;
    }

    public int getVideoHeight() {
        return videoHeight;
    }

    public void setOnVideoCallBack(OnVideoCallBack onVideoCallBack) {
        this.onVideoCallBack = onVideoCallBack;
    }

    public interface OnVideoCallBack {
        void onStartPrepare();

        void onPrepared(long duration);

        void onStart();

        void onPause();

        void onBufferStart();

        void onBufferEnd();

        void onProgress(long progress, long duration);

        void onComplete();

        void onError(String error);
    }

    public void retry() {
        reset();
        setVideoPath(videoPath, header);
    }

    /**
     * 播放出错后，尝试重新播放
     */
    private void retryPlay(int what, String defError) {
        if (timer != null) {
            timer.cancel();
        }
        if (retryCount >= 2) {
            if (onVideoCallBack != null) {
                onVideoCallBack.onError(defError + what);
                onVideoCallBack.onPause();
            }
            retryCount = 0;
            reset();
            LogUtil.e("确实播放出错了，编号：" + what);
        } else {
            retryCount++;
            LogUtil.e("准备重试，第" + retryCount + "次（" + what + "）");
            reset();
            postDelayed(retryRunnable, 3000);
        }
    }

    //超时计时器
    private final CountDownTimer timeOut = new CountDownTimer(INIT_TIME_OUT, 1000) {
        @Override
        public void onTick(long left) {
//            LogUtils.i("超时倒计时：" + (left / 1000));
        }

        @Override
        public void onFinish() {
            onErrorListener.onError(null, MediaPlayer.MEDIA_ERROR_TIMED_OUT, 0);
        }
    };
    //重试倒计时
    private final Runnable retryRunnable = () -> setVideoPath(videoPath, header);
    //错误回调
    private final MediaPlayer.OnErrorListener onErrorListener = (mp, what, extra) -> {
        int p = mp != null ? mp.getCurrentPosition() : 0;
        if (p > 0) {
            currentPosition = p;
        }
        switch (what) {
            case MediaPlayer.MEDIA_ERROR_IO:
                retryPlay(what, "无法加载视频");
                break;
            case MediaPlayer.MEDIA_ERROR_TIMED_OUT:
                retryPlay(what, "加载超时");
                break;
            case MediaPlayer.MEDIA_ERROR_SERVER_DIED:
                retryPlay(what, "拒绝访问");
                break;
            default:
                retryPlay(what, "播放出错，请重试");
                break;
        }
        return true;
    };

    public void setVideoPath(String videoPath, Map<String, String> headers) {
        try {
            if (videoPath == null) {
                return;
            }
            if (!videoPath.equals(this.videoPath)) {
                currentPosition = -1;
            }
            this.videoPath = videoPath;
            this.header = headers;
            reset();
            if (this.header == null) {
                player.setMediaItem(MediaItem.fromUri(videoPath));
            } else {
                MediaItem mediaItem = new MediaItem.Builder().setDrmLicenseRequestHeaders(headers).setUri(videoPath).build();
                player.setMediaItem(mediaItem);
            }
            player.prepare();
//            videoView.setVideoURI(Uri.parse(videoPath), headers);
//            videoView.prepareAsync();
            if (isReady) {
                //曾经准备就绪过，只是因为出错了，要重新准备
                if (onVideoCallBack != null) {
                    onVideoCallBack.onBufferStart();
                }
                LogUtil.i("曾经准备就绪过，只是因为出错了，要重新准备，播放位置为：" + currentPosition);
            } else {
//            lastPausePosition = -1;
                if (onVideoCallBack != null) {
                    onVideoCallBack.onStartPrepare();
                }
            }
            isReady = false;
            timeOut.cancel();
            timeOut.start();
            LogUtil.i("开始准备：" + videoPath);
        } catch (Exception e) {
            LogUtil.e(e.toString());
            if (onVideoCallBack != null) {
                onVideoCallBack.onError(e.toString());
            }
        }

    }

    public void start() {
        if (player == null) {
            LogUtil.e("mediaPlayer不能为空");
            return;
        }
        if (!isReady) {
            return;
        }
        switch (player.getPlaybackState()) {
            case Player.STATE_READY:
            case Player.STATE_BUFFERING:
                player.play();
                break;
            case Player.STATE_ENDED:
            case Player.STATE_IDLE:
                isReady = false;
                currentPosition = -1;
                setVideoPath(videoPath, header);
                player.play();
                break;
        }

    }

    public void pause() {
        if (player == null) {
            LogUtil.e("mediaPlayer不能为空");
            return;
        }
        if (!isReady) {
            return;
        }
        player.pause();
        currentPosition = player.getCurrentPosition();
    }

    public void resume() {
//        mediaPlayer.resume();
        if (currentPosition > 1000) {
            seekTo(currentPosition);
        }
    }

    private void reset() {
        if (player == null) {
            LogUtil.e("mediaPlayer不能为空");
            return;
        }
        try {
            player.stop();
        } catch (Exception e) {
            LogUtil.e(e.toString());
        }

    }

    public void onDestroy() {
        this.reset();
        removeCallbacks(retryRunnable);
        //结束并释放资源
        reset();
        if (timer != null) {
            timer.cancel();
            timer = null;
        }
//        holder.removeCallback(holderCallback);
        timeOut.cancel();
    }

    public void seekTo(long msec) {
        this.seekTo((int) msec);
    }

    public void seekTo(int msec) {
        if (!isReady) {
            return;
        }
        if (player == null) {
            LogUtil.e("mediaPlayer不能为空");
            return;
        }
        player.seekTo(msec);
    }

    public boolean isPlaying() {
        if (player == null) {
            LogUtil.e("mediaPlayer不能为空");
            return false;
        }
        return player.isPlaying();
    }

    public static abstract class MySingleOrDoubleClickListener implements View.OnClickListener {
        /**
         * 双击有效时长（毫秒），建议200~500毫秒内
         */
        private static final long ClickInterval = 210L;
        //上次点击的时间
        private long tLastClick = 0;
        private View v;

        @Override
        public void onClick(View v) {
            this.v = v;
            long t = System.currentTimeMillis();
            if (tLastClick == 0) {
                timer.start();
            } else {
                if (t - tLastClick <= ClickInterval) {
                    onDouble(v);
                    timer.cancel();
                } else {
                    timer.start();
                }
            }
            tLastClick = t;
        }

        /**
         * 单击倒计时
         */
        private final CountDownTimer timer = new CountDownTimer(ClickInterval, ClickInterval) {
            @Override
            public void onTick(long millisUntilFinished) {

            }

            @Override
            public void onFinish() {
                onSingle(v);
            }
        };

        public abstract void onSingle(View v);

        public abstract void onDouble(View v);
    }
}